// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-01-01 // Contains ... using System; using System.Data; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; using System.Xml; using System.Xml.Linq; using JetBrains.Annotations; using LargoCommon.Abstract; using LargoCommon.Interfaces; using LargoCommon.Music; namespace LargoCommon.Support { /// /// MusicXml Measure. /// public sealed class MusicXmlMeasure { #region Fields /// /// Music Xml Header. /// [UsedImplicitly] private MusicXmlHeader header; /// /// Music Xml Reader. /// private MusicXmlReader reader; #endregion #region Constructors /// /// Initializes a new instance of the MusicXmlMeasure class. /// /// Musical header. public MusicXmlMeasure(MusicXmlHeader givenHeader) { this.Header = givenHeader; } /// /// Initializes a new instance of the class. /// [UsedImplicitly] public MusicXmlMeasure() { } #endregion #region Properties /// /// Gets or sets the header. /// /// /// The header. /// /// Music Xml Header is null. /// Music Xml Header cannot be empty.;value public MusicXmlHeader Header { get { Contract.Ensures(Contract.Result() != null); if (this.header == null) { throw new InvalidOperationException("Music Xml Header is null."); } return this.header; } set => this.header = value ?? throw new ArgumentException("Argument cannot be empty.", nameof(value)); } /// /// Gets or sets the reader. /// /// /// The reader. /// private MusicXmlReader Reader { get { Contract.Ensures(Contract.Result() != null); if (this.reader == null) { throw new InvalidOperationException("Music Xml Reader is null."); } return this.reader; } set => this.reader = value ?? throw new ArgumentException("Argument cannot be empty.", nameof(value)); } #endregion #region Public static methods /// /// Measure element. /// /// Bar number. /// Musical track. /// The header. /// /// Returns value. /// public static XElement MeasureElement(int barNumber, MusicalLine track, MusicalHeader header) { Contract.Requires(track != null); var measure = new XElement( "measure", new XAttribute("number", barNumber.ToString(CultureInfo.InvariantCulture))); var beatDivision = header.Division / header.Metric.MetricBeat; var attributes = new XElement( "attributes", new XElement("divisions", beatDivision.ToString(CultureInfo.InvariantCulture))); if (barNumber == 1) { var element = KeyElement(); //// track attributes.Add(element); element = TimeElement(header); attributes.Add(element); element = ClefElement(track); if (!element.IsEmpty) { attributes.Add(element); } //// element = this.TempoElement(); //// attributes.Add(element); } var tones = track.MusicalTonesInBar(barNumber); // ReSharper disable once LoopCanBePartlyConvertedToQuery foreach (var tone in tones) { var mtone = tone as MusicalTone; var noteElement = mtone?.Pitch != null ? NoteElement(mtone, beatDivision) : RestElement(tone, beatDivision); measure.Add(noteElement); } measure.Add(attributes); return measure; } #endregion #region Public methods /// /// Read Musical Bar. /// /// The score part object. /// Musical Part. /// Musical measure. /// Bar number. /// The given reader. public void ReadMusicalBar(ScorePartObject scorePartObject, MusicalPart musicalPart, XContainer measure, int barNumber, MusicXmlReader givenReader) { Contract.Requires(measure != null); Contract.Requires(musicalPart != null); this.Reader = givenReader; var attributes = measure.Element("attributes"); //// XElement key = attributes.Element("key"); //// XElement clef = attributes.Element("clef"); var transpose = attributes?.Element("transpose"); if (transpose != null) { this.Reader.LocalPitchShift = 0; //// XElement diatonic = transpose.Element("diatonic"); //// XElement octaveDouble = transpose.Element("double"); var octaveChange = transpose.Element("octave-change"); if (octaveChange != null) { this.Reader.LocalPitchShift += (int)octaveChange * DefaultValue.HarmonicOrder; } var chromatic = transpose.Element("chromatic"); if (chromatic != null) { this.Reader.LocalPitchShift += (int)chromatic; } } var actions = measure.Nodes(); //// Elements("note"); foreach (var action in actions.Where(node => node.NodeType == XmlNodeType.Element).OfType()) { this.AppendMusicalTones(scorePartObject, musicalPart, barNumber, action); } } #endregion #region Private static methods /// /// Note element. /// /// Melodic tone. /// Beat division. /// Returns value. private static XElement NoteElement(MusicalTone mtone, int beatDivision) { Contract.Requires(mtone != null); Contract.Requires(mtone.Pitch != null); var quotient = 1.0f * mtone.Duration / beatDivision; var noteType = MusicXmlHeader.GetNoteType(quotient); var pitch = new XElement( "pitch", new XElement("step", MusicalProperties.GetSingleNoteName(mtone.Pitch.Element)), new XElement("alter", MusicalProperties.GetAlterSign(mtone.Pitch.Element).ToString(CultureInfo.InvariantCulture)), new XElement("octave", mtone.Pitch.StandardOctave.ToString(CultureInfo.InvariantCulture))); var note = new XElement( "note", pitch, new XElement("duration", mtone.Duration.ToString(CultureInfo.InvariantCulture)), new XElement("type", noteType)); return note; } /// /// Note element. /// /// Musical Tone. /// Beat division. /// Returns value. private static XElement RestElement(IMusicalTone mtone, int beatDivision) { Contract.Requires(mtone != null); var quotient = 1.0f * mtone.Duration / beatDivision; var noteType = MusicXmlHeader.GetNoteType(quotient); var note = new XElement( "note", new XElement("rest", null), new XElement("duration", mtone.Duration.ToString(CultureInfo.InvariantCulture)), new XElement("type", noteType)); return note; } /// /// Read Musical shift. /// /// Musical shift. /// Bar number. /// Sign of the shift. /// Time quotient. /// Returns value. private static MusicalShift ReadMusicalShift(XContainer shift, int barNumber, short signum, double quotient) { Contract.Requires(shift != null); var durationElement = shift.Element("duration"); if (durationElement == null) { return null; } var musicalShift = new MusicalShift { BarNumber = barNumber }; var duration = (int)((int)durationElement * quotient); musicalShift.Value = (short)(signum * duration); var staff = shift.Element("staff"); if (staff != null) { musicalShift.Staff = (byte)(int)staff; } var voice = shift.Element("voice"); if (voice != null) { musicalShift.Voice = (byte)(int)voice; } return musicalShift; } /// /// Key element. /// /// Returns value. private static XElement KeyElement() { //// MusicalLine track const int fifthNumber = 0; var key = new XElement( "key", new XElement("fifths", fifthNumber.ToString(CultureInfo.InvariantCulture)), new XElement("mode", "major")); return key; } /// /// Time element. /// /// The header. /// /// Returns value. /// private static XElement TimeElement(MusicalHeader header) { var key = new XElement( "time", new XElement("beats", header.Metric.MetricBeat.ToString(CultureInfo.InvariantCulture)), new XElement("beat-type", header.Metric.MetricGround.ToString(CultureInfo.InvariantCulture))); return key; } /// /// Clef element. /// /// Musical track. /// Returns value. private static XElement ClefElement(MusicalLine track) { Contract.Requires(track != null); var clef = new XElement("clef"); var bandType = MusicalProperties.BandTypeFromOctave(track.FirstStatus.Octave); //// (MusicalBand)track.BandType switch (bandType) { case MusicalBand.HighBeat: { clef.Add(new XElement("sign", "G")); clef.Add(new XElement("line", "2")); } break; case MusicalBand.MiddleBeat: { clef.Add(new XElement("sign", "G")); clef.Add(new XElement("line", "2")); } break; case MusicalBand.BassTones: { clef.Add(new XElement("sign", "F")); clef.Add(new XElement("line", "4")); } break; case MusicalBand.Any: break; case MusicalBand.MiddleTones: break; case MusicalBand.HighTones: break; case MusicalBand.BassBeat: break; //// resharper default: break; } return clef; } #endregion #region Private methods /// /// Appends the musical tones. /// /// The score part object. /// The musical part. /// The bar number. /// The action. private void AppendMusicalTones(ScorePartObject scorePartObject, MusicalPart musicalPart, int barNumber, XElement action) { Contract.Requires(musicalPart != null); Contract.Requires(action != null); double quotient = 1.0f * this.Reader.CommonDivision / this.Reader.LocalDivision; //// this.Reader.MusicalBlock.RhythmicOrder; //// ; quotient = quotient * this.Reader.Block.Header.System.RhythmicOrder / this.Reader.CommonDivision; if (action.Name == "backward") { var shift = ReadMusicalShift(action, barNumber, -1, quotient); if (shift != null) { musicalPart.AddMusicalObject(shift); } } if (action.Name == "forward") { var shift = ReadMusicalShift(action, barNumber, +1, quotient); if (shift != null) { musicalPart.AddMusicalObject(shift); } } // ReSharper disable once InvertIf if (action.Name == "note") { var tone = this.ReadMusicalTone(action, barNumber, quotient); var chord = action.Element("chord"); if (chord != null) { if (tone != null) { var shift = new MusicalShift { BarNumber = barNumber, Value = (short)-tone.Duration, Voice = tone.Voice, Staff = tone.Staff }; musicalPart.AddMusicalObject(shift); } } if (tone == null) { return; } tone.InstrumentNumber = scorePartObject.MidiProgram; //// tone.Channel = (MidiChannel)scorePartObject.MidiChannel; musicalPart.AddMusicalObject(tone); } } /// /// Read Musical Tone. /// /// Musical note. /// Bar number. /// Time quotient. /// Returns value. private MusicalStrike ReadMusicalTone(XContainer note, int barNumber, double quotient) { Contract.Requires(note != null); MusicalStrike tone; var pitch = note.Element("pitch"); //// XElement rest = note.Element("rest"); var durationElement = note.Element("duration"); if (durationElement == null) { return null; } var duration = (int)((int)durationElement * quotient); //// in ticks //// BitRange range = new BitRange(this.Reader.MusicalBlock.RhythmicOrder, bitFrom, duration); if (pitch != null) { var step = (string)pitch.Element("step"); var alterString = (string)pitch.Element("alter"); var alter = (short)(alterString != null ? short.Parse(alterString, CultureInfo.InvariantCulture) : 0); var octave = (string)pitch.Element("octave"); //// byte.Parse(step) var noteNumber = MusicalProperties.GetNoteNumber(step, alter); var element = noteNumber >= 0 ? (byte)(noteNumber % this.Reader.Block.Header.System.HarmonicOrder) : (byte)(noteNumber + this.Reader.Block.Header.System.HarmonicOrder); if (element >= DefaultValue.HarmonicOrder) { throw new DataException("Invalid Musical Element"); } var hs = HarmonicSystem.GetHarmonicSystem(this.Reader.Block.Header.System.HarmonicOrder); var musicalPitch = new MusicalPitch(hs, (short)(short.Parse(octave, CultureInfo.InvariantCulture) + 1), element); if (this.Reader.LocalPitchShift != 0) { musicalPitch.SetAltitude(musicalPitch.SystemAltitude + this.Reader.LocalPitchShift); } tone = new MusicalTone(musicalPitch, this.Reader.Block.Header.System.RhythmicOrder, (byte)duration, MusicalLoudness.MeanLoudness, barNumber); } else { tone = new MusicalStrike(MusicalToneType.Empty, this.Reader.Block.Header.System.RhythmicOrder, (byte)duration, MusicalLoudness.None, barNumber); } var staff = note.Element("staff"); if (staff != null) { tone.Staff = (byte)(int)staff; } var voice = note.Element("voice"); if (voice != null) { tone.Voice = (byte)(int)voice; } return tone; } #endregion } }